/*
 * 
 * Copyright 2010 David H Pollack
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * 
 * 
 */

package com.dhpollack.rotogss.servlets;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;

import net.oauth.OAuthException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.dhpollack.rotogss.data.AppConfig;
import com.dhpollack.rotogss.data.RotoGSSUsers;
import com.dhpollack.rotogss.data.YDraft;
import com.dhpollack.rotogss.data.YPlayers;
import com.dhpollack.rotogss.data.YahooGames;
import com.dhpollack.rotogss.data.YahooLeagueTeams;
import com.dhpollack.rotogss.data.YahooLeagues;
import com.dhpollack.rotogss.helper.AppHelper;
import com.dhpollack.rotogss.helper.XPATH;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Text;
import com.simpleyql.Api;
import com.simpleyql.ApiFactory;
import com.simpleyql.QueryResult;

/**
 * @author davidpl
 *
 */
public class YStoreDraftServlet extends HttpServlet {
	
	private static final long serialVersionUID = 5636179829235840164L;

	private static final AppConfig appConfig = AppHelper.get();
	
	private static XPath xPath = XPATH.get().newXPath();
	
	private DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	
	private String user;
	
	private Key leagueKey;
	
	private String leagueString;
	
	private Map<String, Key> keyMap = new HashMap<String, Key>();
	
	private String authData;
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		doPost(req, resp);
	}

	public void doPost(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {

		if((user = req.getParameter("user")) == null) {
			log("User Not Set");
			return;
		}

		if((leagueString = req.getParameter("league")) == null) {
			log("User Not Set");
			return;
		} else {
			setGameLeagueKeys(leagueString);
		}
		
		if(!getUserAuthData()) {
			log("Authdata Not Found");
			return;
		}
		

		Entity l = null;
		
		try {
			l = datastore.get(leagueKey);
			String draftType = (String) l.getProperty("draftType");
			log("Draft Type: " + draftType);
		} catch (EntityNotFoundException e) {
			log("League Not Found");
			e.printStackTrace();
			return;
		}
		
		String urlString = buildYahooUrl();

		String draftXml = getDraftXml(urlString);
		
		List<String> xpathExps = new ArrayList<String>();
		xpathExps.add("pick");
		xpathExps.add("round");
		xpathExps.add("team_key");
		xpathExps.add("player_key");

		List<Entity> draftResults = new ArrayList<Entity>();
		
		try {
			List<XPathExpression> xpaths = createXPathList(xpathExps);
			NodeList draftNodes = parseYahooXml(draftXml, "//league/draft_results/draft_result");
			int draftCount = draftNodes.getLength();
			log("Node Count: " + draftCount);
			for(int i=0;i<draftCount;i++) {
				Entity draftEntry = createYDraft(draftNodes.item(i), xpaths);
				draftResults.add(draftEntry);
			}
		} catch (XPathExpressionException e) {
			log("XPath Error");
			e.printStackTrace();
		} catch (SAXException e) {
			log("SAX Error");
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			log("ParserConfig Error");
			e.printStackTrace();
		}
			
		datastore.put(draftResults);

	}
	

	private boolean getUserAuthData() {
		Key userKey = KeyFactory.createKey(RotoGSSUsers.class.getSimpleName(), user);
		try {
			Entity gss_user = datastore.get(userKey);
			Text authText = (Text) gss_user.getProperty("yahooOAuthData");
			authData = authText.getValue();
		} catch(EntityNotFoundException e) {
			log("User Not Found In Datastore");
			return false;
		}
		
		return true;
		
	}
	
	private void setGameLeagueKeys(String leagueString) {
		// Processing for game key.  Will be replaced with game parse
		String regex = "\\.";
		Pattern numSplit = Pattern.compile(regex);
		String[] m = numSplit.split(leagueString);
		int g = Integer.parseInt(m[0]);

		leagueKey = new KeyFactory.Builder(YahooGames.class.getSimpleName(), g)
								.addChild(YahooLeagues.class.getSimpleName(), leagueString)
								.getKey();
		
	}
	
	private String buildYahooUrl() {
		StringBuilder s = new StringBuilder();
		
		s.append("http://fantasysports.yahooapis.com/fantasy/v2/league/");
		s.append(leagueString);
		s.append("/draftresults");
		
		return s.toString();
	}

	private String getDraftXml(String url) throws IOException {
		
		Api api = ApiFactory.getApiInstance(
				appConfig.getYahooConsumerKey(),
				appConfig.getYahooConsumerSecret(),
				appConfig.getBaseUrl() + "/simpleyqlcallback/",
	            false, null);
		
		QueryResult qr = null;
		
		try {
			qr = api.query2(url, authData);
		} catch (OAuthException e) {
			log("Error posting to Y!:", e);
			return null;
		} catch (RuntimeException e) {
			if (e.getMessage().equals("Unable to complete the HTTP request")) {
				if (e.getCause().getMessage()
						.contains("Timeout while fetching")) {
					log("Unable to complete Yahoo! query", e);
				} else {
					log("Error posting to Y!:", e);
					return null;
				}
			} else {
				throw e;
			}
		}
		
		// Update token, if needed (for future requests)
	    if (qr != null && !qr.getAuthdata().equals(authData)) {
	    	authData = qr.getAuthdata();
			Key userKey = KeyFactory.createKey(RotoGSSUsers.class.getSimpleName(), user);
	    	Text newAuthdata = new Text(authData);
			try {
				Entity gss_user = datastore.get(userKey);
		    	gss_user.setProperty("yahooOAuthData", newAuthdata);
		    	datastore.put(gss_user);
			} catch (EntityNotFoundException e) {
				e.printStackTrace();
			}
	    }
	    

		return qr.getText();
	}

	private List<XPathExpression> createXPathList(List<String> xpathExps) throws XPathExpressionException {
		List<XPathExpression> ret = new ArrayList<XPathExpression>();
		for(String xpathExp : xpathExps) ret.add(xPath.compile(xpathExp));
		
		return ret;
	}
	
	private NodeList parseYahooXml(String xml, String expression)
			throws SAXException, IOException
			, ParserConfigurationException, XPathExpressionException {
		InputSource is = new InputSource(new StringReader(xml));
		Document response = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
		NodeList n = (NodeList)xPath.evaluate( expression , response, XPathConstants.NODESET);
		return n;
	}

	private Entity createYDraft(Node node, List<XPathExpression> xpaths)
			throws XPathExpressionException {
		String pickString = (String) xpaths.get(0).evaluate(node, XPathConstants.STRING);
		String roundString = (String) xpaths.get(1).evaluate(node, XPathConstants.STRING);
		String teamKey = (String) xpaths.get(2).evaluate(node, XPathConstants.STRING);
		String playerKey = (String) xpaths.get(3).evaluate(node, XPathConstants.STRING);
		
		Integer pick = Integer.valueOf(pickString);
		Integer round = Integer.valueOf(roundString);
		
		if(!keyMap.containsKey(teamKey)) {
			Key k = KeyFactory.createKey(leagueKey, YahooLeagueTeams.class.getSimpleName(), teamKey);
			keyMap.put(teamKey, k);
		}
		
		Key pKey = KeyFactory.createKey(YPlayers.class.getSimpleName(), playerKey);

		Key entKey = KeyFactory.createKey(leagueKey, YDraft.class.getSimpleName(), pick.longValue());
	
		Entity ret = new Entity(entKey);
		ret.setProperty("leagueKey", leagueKey);
		ret.setProperty("teamKey", keyMap.get(teamKey));
		ret.setProperty("playerKey", pKey);
		ret.setProperty("pick", pick);
		ret.setProperty("round", round);
		
		return ret;
	}

}
